package com.auxgroup.smarthome.business.controller;

import com.alibaba.fastjson.JSONObject;
import com.auxgroup.bridge.app.inner.vo.DeviceFaultVo;
import com.auxgroup.bridge.business.inner.vo.DeviceExportDataVo;
import com.auxgroup.bridge.business.inner.vo.DeviceListVo;
import com.auxgroup.bridge.business.inner.vo.DeviceUserListVo;
import com.auxgroup.smarthome.business.service.DeviceService;
import com.auxgroup.smarthome.business.service.inner.AppUserDBInnerService;
import com.auxgroup.smarthome.business.service.inner.DeviceFaultInnerService;
import com.auxgroup.smarthome.business.util.DownloadForResponseUtil;
import com.auxgroup.smarthome.business.vo.DeviceDetailVo;
import com.auxgroup.smarthome.business.vo.DeviceExportTitleVo;
import com.auxgroup.smarthome.enterprise.service.AuxAnalysisService;
import com.auxgroup.smarthome.enterprise.service.DeviceManageService;
import com.auxgroup.smarthome.enterpriseapi.requestbody.DeviceAttrsDataBody;
import com.auxgroup.smarthome.enterpriseapi.requestbody.DevicesOnlineDataBody;
import com.auxgroup.smarthome.enterpriseapi.requestbody.auxrequest.AuxDeviceAggDataBody;
import com.auxgroup.smarthome.enterpriseapi.responsebody.DeviceAttrsData;
import com.auxgroup.smarthome.enterpriseapi.responsebody.DeviceOnoffline;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxAggDataResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.ExtDatapointResult;
import com.auxgroup.smarthome.filter.annotation.PermissionFilter;
import com.auxgroup.smarthome.syscode.BusinessCode;
import com.auxgroup.smarthome.syscode.Syscode;
import com.auxgroup.smarthome.utils.JsonValidator;
import com.auxgroup.smarthome.utils.PageContent;
import com.auxgroup.smarthome.utils.excel.Excel;
import com.auxgroup.smarthome.utils.excel.constant.SheetVersion;
import com.auxgroup.smarthome.web.ApiResponse;
import com.google.gson.Gson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @Author: laiqiuhua.
 * @Date: 2017/8/14 9:15.
 */
@Api(tags = "设备管理", description = "web设备相关")
@RestController
@RequestMapping("/products")
@Validated
public class DeviceManageController {
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private AuxAnalysisService auxAnalysisService;
    @Autowired
    private DeviceManageService deviceManageService;
    @Autowired
    private DeviceFaultInnerService deviceFaultInnerService;
    @Autowired
    private AppUserDBInnerService appUserDBInnerService;

    @ApiOperation(value = "获取设备列表分页")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "modelId", value = "型号Id", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "deviceStatus", value = "设备状态:\"\"代表全部，0不在线，1在线", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "keywords", value = "用mac/did/sn进行模糊查询", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "page", value = "页码", paramType = "query", dataType = "integer", defaultValue = "0"),
            @ApiImplicitParam(name = "size", value = "分页大小", paramType = "query", dataType = "integer", defaultValue = "20")
    })
    @GetMapping("/devices")
    public ApiResponse<Page<DeviceListVo>> getDeviceList(@RequestParam(value = "modelId", required = false) String modelId, @ApiIgnore String keywords, @ApiIgnore String deviceStatus, @ApiIgnore Integer page, @ApiIgnore Integer size) {
        return deviceService.getDeviceList(modelId, keywords, deviceStatus, page, size);
    }

    @ApiOperation(value = "获取设备列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "modelId", value = "型号Id", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "deviceStatus", value = "设备状态:\"\"代表全部，0不在线，1在线", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "keywords", value = "用mac/did/sn进行模糊查询", paramType = "query", dataType = "string")
    })
    @GetMapping("/devices/list")
    public ApiResponse<Page<DeviceListVo>> getDeviceListByNotPage(@RequestParam(value = "modelId", required = false) String modelId, @ApiIgnore String keywords, @ApiIgnore String deviceStatus, @ApiIgnore Integer page, @ApiIgnore Integer size) {
        return deviceService.getDeviceList(modelId, keywords, deviceStatus, page, size);
    }

    @ApiOperation(value = "获取设备详情")
    @GetMapping("{productKey}/devices/{did}")
    public ApiResponse<DeviceDetailVo> getDeviceDetail(@PathVariable("productKey") String productKey, @PathVariable("did") String did) {
        return deviceService.getDeviceDetail(productKey, did);
    }

    @ApiOperation(value = "获取设备滤网寿命")
    @GetMapping("/devices/{mac}/mesh_life")
    public ApiResponse<Integer> getMeshLife(@PathVariable("mac") String mac) {
        return deviceService.getMeshLife(mac);
    }

    @ApiOperation(value = "导出设备数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "modelId", value = "型号Id", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "deviceStatus", value = "设备状态:\"\"代表全部，0不在线，1在线", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "keywords", value = "用mac/did/sn进行模糊查询", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "exportType", value = "导出类型：0：全部批量，1：筛选结果", paramType = "query", dataType = "int", required = true),
            @ApiImplicitParam(name = "exportFields", value = "导出字段JSON示例：{\"mac\" : \"设备mac\"}", paramType = "query", dataType = "string", required = true)
    })
    @GetMapping("/devices/export")
    @PermissionFilter(filter = true)
    public ApiResponse getDeviceExport(String modelId, String deviceStatus, String keywords, @NotNull(message = "exportType不能为空") @Range(min = 0, max = 1, message = "exportType必选传0或1") int exportType, @NotBlank(message = "导出字段必填") String exportFields, @ApiIgnore HttpServletResponse response) throws Exception {
        if (!JsonValidator.INSTANCE.validate(exportFields)) {
            return ApiResponse.prompt(BusinessCode.DTO_PARAMS_ERROR.getCode(), "exportFields不是有效的json");
        }

        // 支持的导出字段
        final List<String> AllExportFields = Arrays.asList(new String[] {"mac", "did", "sn", "online", "meshLife", "latestOnlineTime"});

        Gson gson = new Gson();
        Map<String, String> titleMap = gson.fromJson(exportFields, Map.class);
        List<String> titleList = new ArrayList<>(titleMap.keySet());

        // 不支持的字段
        StringBuilder unsupportFields = new StringBuilder();

        titleList.stream().forEach(title -> {
            if (!AllExportFields.contains(title)) {
                unsupportFields.append(title).append(",");
            }
        });

        if (unsupportFields.length() != 0) {
            unsupportFields.append("不支持的导出字段");
            return ApiResponse.prompt(BusinessCode.DTO_PARAMS_ERROR.getCode(), unsupportFields.toString());
        }

        // exportType = 0 导出全部数据
        if(exportType == 0) {
            modelId ="";
            deviceStatus = "";
            keywords = "";
        }

        ApiResponse<List<DeviceExportDataVo>> apiResponse = deviceService.getDeviceExportData(modelId, deviceStatus, keywords);
        if (apiResponse.getCode() != 200) {
            return apiResponse;
        }

        List<DeviceExportDataVo> deviceListVos = apiResponse.getData();

        List<DeviceExportTitleVo> exportData = new ArrayList<>();

        for(DeviceExportDataVo deviceExportDataVo: deviceListVos) {
            exportData.add(DeviceExportTitleVo.createExportData(deviceExportDataVo));
        }

        Excel excel = new Excel(SheetVersion.EXCEL2007_SXSSF);

        // 设置标题，水平对齐
        excel.row(0)
                .value(titleMap.values().toArray())
                .bold()
                .align(HorizontalAlignment.CENTER);

        DownloadForResponseUtil.setExcelData(excel, exportData, titleList, DeviceExportTitleVo.class);


        //冻结第一行, 重命名当前处于工作状态的表的名称
        excel.sheet().freeze(1, 0)
                .sheetName("设备列表");

        DownloadForResponseUtil.downloadExcel(excel.getWorkbook(), "aux_devices", response);
        return null;
    }

    @ApiOperation(value = "获取设备实时数据", notes = "")
    @GetMapping("/{productKey}/devices/{did}/latest_data")
    public ApiResponse<DeviceAttrsData> getLatestData(@PathVariable("productKey") String productKey, @PathVariable("did") String did) {
        return deviceManageService.getLastestDevice(productKey, did);
    }


    @ApiOperation(value = "获取设备70分钟以内的数据", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "attrs", value = "数据点,请注意命名规则，多个数据点用英文逗号分隔", paramType = "query", dataType = "string")
    })
    @GetMapping("/{productKey}/devices/{did}/history_data")
    public ApiResponse<Map<Integer, Integer>> getHistoryDataInterval70Minutes(@PathVariable("productKey") String productKey, @PathVariable("did") String did, String attrs) {
        DeviceAttrsDataBody body = new DeviceAttrsDataBody();
        if (StringUtils.isNotBlank(attrs)) {
            if (!auxAnalysisService.isHasDatapoint(productKey, attrs)) {
                return ApiResponse.prompt(Syscode.FAIL.getCode() + "", "数据点不存在！");
            }
            body.setAttrs(attrs);
        }
        Instant endTs = Instant.now(Clock.system(ZoneId.of("Asia/Shanghai")));
        Instant startTs = endTs.minus(70, ChronoUnit.MINUTES);
        body.setStart_ts(Long.valueOf(startTs.getEpochSecond()).intValue());
        body.setEnd_ts(Long.valueOf(endTs.getEpochSecond()).intValue());
        body.setLimit(20);
        body.setSkip(0);
        return deviceManageService.getAuxDeviceHistoryData(productKey, did, body, attrs);
    }


    @ApiOperation(value = "获取聚合数据", notes = "设备聚合数据\n" +
            "start 和 end 为字符串日期，日期格式为年月日时，For Example: \"2017072111\"。\n" +
            "attrs 只能为数字类型的数据点。\n" +
            "返回的 datetime 格式\n" +
            "HOURS \"2015072010\"\n" +
            "DAYS \"20150720\"\n" +
            "WEEKS \"201529\"\n" +
            "MONTHS \"201507\"\n" +
            "YEARS \"2017\"\n" +
            "aggregator include [sum,avg,max,min]\n" +
            "unit 包含 [HOURS,DAYS,WEEKS,MONTHS,YEARS]，unit 最小的单位为 HOURS")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "start", value = "时间格式：yyyyMMddHHmm", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "end", value = "时间格式：yyyyMMddHHmm", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "attrs", value = "数据点标识 可以是多个 ", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "aggregator", value = "sum,avg,max,min", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "unit", value = "HOURS,DAYS,WEEKS,MONTHS", dataType = "string", paramType = "query", required = true)
    })
    @GetMapping("/{productKey}/devices/{did}/run_curve")
    public ApiResponse<AuxAggDataResult> getRunCurveList(@PathVariable("productKey") String productKey, @PathVariable("did") String did, @ApiIgnore AuxDeviceAggDataBody auxDeviceAggDataBody) {
        return auxAnalysisService.wifiDeviceAggData(productKey, did, auxDeviceAggDataBody);
    }

    @ApiOperation(value = "获取上下线记录数据", notes = "按 ts 降序排序。\n" +
            "\" +\n" +
            "            \"设备的心跳统计信息，在设备上下线记录的 Payload 中。\\n\" +\n" +
            "            \"设备上线记录中，payload 记录了设备设备指定的心跳时间间隔（KeepAlive），单位为秒，此 KeepAlive 的值等于设备上传的心跳时间间隔再加上5秒\\n\" +\n" +
            "            \"设备下线记录中，payload 记录了设备的在线时长（duration），以及在线时发送的心跳次数（heartbeat/count），相邻两个心跳的最大间隔时间（heartbeat/max），最小间隔时间（heartbeat/min），平均间隔时间（heartbeat/avg）, 最后一次收到心跳时刻与下线时刻的间隔时间（heartbeat/last），单位为秒\\n\" +\n" +
            "            \"心跳的间隔时间，为m2m收到第n次心跳的时间点（时间戳）与第n-1次心跳的时间点（时间戳）之差，其中n >= 2；当n < 2时，max，min，avg的值固定为0 max，min：计算客户端从接到第一次心跳开始，到最后一次接到心跳为止的时间段内，相邻两个心跳时间差的最大，最小值；\\n\" +\n" +
            "            \"avg：客户端与云端建立链接开始，到最后一次接到心跳为止的时间段，除以心跳次数；\\n\" +\n" +
            "            \"设备下线记录中，payload记录了设备的下线原因（reason），说明各种reason的意义：\\n\" +\n" +
            "            \"mqtt_disconnect：设备主动断开与mqtt的连接\\n\" +\n" +
            "            \"no_heartbeat：m2m在KeepAlive时段内，没有收到设备心跳\\n\" +\n" +
            "            \"tcp_closed：设备主动断开tcp连接\\n\" +\n" +
            "            \"ssl_closed：设备主动断开ssl连接\\n\" +\n" +
            "            \"offline_force：设备重复上线，原有的连接断开\\n\" +\n" +
            "            \"offline_reset：设备注销，断开连接\\n\" +\n" +
            "            \"offline_exception：异常断开连接\\n\" +\n" +
            "            \"offline_sending_density_overflow：客户端发送信息的频率过大，断开链接\\n\" +\n" +
            "            \"offline_sending_data_size_overflow：客户端发送信息的流量过大，断开链接")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "start_ts", value = "开始时间戳", dataType = "integer", paramType = "query"),
            @ApiImplicitParam(name = "end_ts", value = "结束时间戳", dataType = "integer", paramType = "query"),
            @ApiImplicitParam(name = "sort", value = "排序，asc升序，desc降序 ", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "limit", value = "每页容量", dataType = "integer", paramType = "query", defaultValue = "10"),
            @ApiImplicitParam(name = "skip", value = "页索引", dataType = "integer", paramType = "query", defaultValue = "0")
    })
    @GetMapping("/{productKey}/devices/{did}/online")
    public ApiResponse<DeviceOnoffline> getDeviceOnline(@PathVariable("productKey") String productKey, @PathVariable("did") String did, @ApiIgnore DevicesOnlineDataBody devicesOnlineDataBody) {
        return deviceManageService.getDeviceOnoffData(productKey, did, devicesOnlineDataBody);
    }

    @ApiOperation(value = "远程控制设备", notes = "返回数据详见企业api")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "body", value = "原始指令:  {\"raw\":[1,2,3]} 或者 数据点方式:  {\"attrs\":{\"temp\":30}}", paramType = "body")
    })
    @RequestMapping(value = "/app/products/{productKey}/devices/{did}/control", method = RequestMethod.POST)
    public ApiResponse<JSONObject> controlRemoteDevice(@PathVariable("productKey") String productKey,
                                                       @PathVariable("did") String did,
                                                       @RequestBody String body) {
        return ApiResponse.ok(deviceManageService.controlRemoteDevice(productKey, did, body));
    }

    @ApiOperation(value = "获取透传产品数据点")
    @GetMapping("/{productKey}/devices/ext_datapoint")
    public ApiResponse<List<ExtDatapointResult>> getDeviceExtDatapoint(@PathVariable("productKey") String productKey) {
        return auxAnalysisService.getProductExtDatapoint(productKey);
    }

    @ApiOperation(value = "获取设备用户列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "页码", paramType = "query", dataType = "integer", defaultValue = "0"),
            @ApiImplicitParam(name = "size", value = "分页大小", paramType = "query", dataType = "integer", defaultValue = "20")
    })
    @GetMapping("/devices/{mac}/users")
    public ApiResponse<Page<DeviceUserListVo>> getDeviceUserList(@PathVariable("mac") String mac, @ApiIgnore Integer page, @ApiIgnore Integer size) {
        ApiResponse<PageContent<DeviceUserListVo>> apiResponse = appUserDBInnerService.getDeviceUserList(mac, page, size);
        Page<DeviceUserListVo> pageContent = apiResponse.getData().convertPage();
        return new ApiResponse(apiResponse.getCode(), apiResponse.getMessage(), pageContent);
    }

    @ApiOperation(value = "获取web设备故障列表")
    @GetMapping("/devices/{mac}/faults")
    public ApiResponse<List<DeviceFaultVo>> getDeviceFaultList(@PathVariable("mac") String mac) {
        return deviceFaultInnerService.getAppDeviceFaultList(mac);
    }

}
